package com.libhttp

import android.annotation.SuppressLint
import android.content.Context
import android.icu.util.TimeZone
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import androidx.collection.ArrayMap
import androidx.collection.arrayMapOf
import androidx.lifecycle.LifecycleOwner
import androidx.multidex.BuildConfig
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.StringUtils
import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.Utils
import com.rxjava.rxlife.lifeOnMain
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import okhttp3.OkHttpClient
import rxhttp.RxHttpPlugins
import rxhttp.async
import rxhttp.toAwait
import rxhttp.wrapper.converter.MoshiConverter
import rxhttp.wrapper.exception.ParseException
import rxhttp.wrapper.param.RxHttp
import rxhttp.wrapper.param.toObservableResponseData
import rxhttp.wrapper.ssl.HttpsUtils
import java.io.File
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLSession

/**
 * CreateBy:Joker
 * CreateTime:2023/4/28 11:17
 * description：
 */
object HttpManager {
    /**
     * http请求超时配置
     */
    private const val READ_TIME_OUT_SECONDS = 60L
    private const val WRITE_TIME_OUT_SECONDS = 60L
    private const val TIME_OUT_SECONDS = 60L

    const val CODE_SUCCESS = 200
    const val CODE_FAIL = -1

    const val TAG = "HttpManager"
    private val mapCommonParams = arrayMapOf<String, Any?>()
    private var userParamsCallback: (() -> MutableMap<String, Any?>)? = null

    private var okHttpClient: OkHttpClient? = null

    private var offsetTime = 0L
    var serverTime: Long = 0
        get() {
            return System.currentTimeMillis() + offsetTime
        }
        set(value) {
            field = value
            offsetTime = field - System.currentTimeMillis()
        }

    fun init(userParamsCallback: () -> MutableMap<String, Any?>) {
        this.userParamsCallback = userParamsCallback
        //证书配置，可以访问所有 具体：https://github.com/liujingxing/rxhttp/wiki/%E5%85%B3%E4%BA%8EHttps
        val sslParams = HttpsUtils.getSslSocketFactory()
        okHttpClient = OkHttpClient.Builder()
            .connectTimeout(TIME_OUT_SECONDS, TimeUnit.SECONDS)
            .readTimeout(READ_TIME_OUT_SECONDS, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIME_OUT_SECONDS, TimeUnit.SECONDS)
            .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
            .addInterceptor(ParamInterceptor())
            .hostnameVerifier { hostname: String?, session: SSLSession? ->
                if (BuildConfig.DEBUG) LogUtils.vTag(TAG, "hostnameVerifier hostname=$hostname session=$session")
                true
            }
            .build()

        RxHttpPlugins.init(okHttpClient)                                      //自定义OkHttpClient对象
            .setDebug(false, true, 2)    //是否开启调试模式，开启后，logcat过滤RxHttp，即可看到整个请求流程日志
            .setConverter(MoshiConverter.create())
            //.setExcludeCacheKeys("time")                                    //设置一些key，不参与cacheKey的组拼
            //.setResultDecoder(s -> s)                                       //设置数据解密/解码器，非必须
            .setOnParamAssembly {                                             //设置公共参数，非必须
                //1、可根据不同请求添加不同参数，每次发送请求前都会被回调
                //2、如果希望部分请求不回调这里，发请求前调用RxHttp#setAssemblyEnabled(false)即可
//                val method = it.method
//                if (method.isGet) {         //Get请求
//                    it.add("method", "get")
//                } else if (method.isPost){ //Post请求
//                    it.add("method", "post")
//                }

                it.addHeader("platform", "android")          //添加公共请求头
                it.addAllQuery(getCommonParamMap())
            }
    }

    /**
     * get请求
     */
    inline fun <reified T> get(owner: LifecycleOwner, url: String, map: ArrayMap<String, Any?>? = null, noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null) {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "请求get=$url 参数=${newMap}")
        //重试2次，每次间隔1s   //如果是网络异常就重试
        RxHttp.get(url)
            .addAll(newMap)
            .toObservableResponseData<T>()
            .lifeOnMain(owner)
            .subscribe({ data -> reqResult?.invoke(CODE_SUCCESS, data, "") }, { error -> onFail(url, error, reqResult) })
    }

    /**
     * get协程 多个合并并发请求
     *  //启动协程，发送请求
     *  lifecycleScope.launch { sendRequest(this) }
     *
     *  //当前在UI线程运行
     * suspend fun sendRequest(scope: CoroutineScope) {
     *     val asyncBanner = getBanners(scope) //这里返回Deferred<List<Banner>>对象
     *     val asyncPersons = getStudents(scope) //这里返回Deferred<List<Student>>对象
     *     //开始更新UI
     * }
     */
    suspend inline fun <reified T> getSync(scope: CoroutineScope, url: String, map: ArrayMap<String, Any?>? = null): Deferred<T?> {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "请求getSync=$url 参数=${newMap}")

        return RxHttp.get(url)
            .addAll(newMap)
            .toAwait<T>()
            .async(scope)
    }

    /**
     * post表单提交
     */
    inline fun <reified T> postForm(owner: LifecycleOwner, url: String, map: ArrayMap<String, Any?>? = null, noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null) {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "请求postForm=$url 参数=${newMap}")

        RxHttp.postForm(url)
            .addAll(newMap)
            .toObservableResponseData<T>()
            .lifeOnMain(owner)
            .subscribe({ data -> reqResult?.invoke(CODE_SUCCESS, data, "") }, { error -> onFail(url, error, reqResult) })
    }

    /**
     * postJson提交
     */
    inline fun <reified T> postJson(owner: LifecycleOwner, url: String, map: ArrayMap<String, Any?>? = null, noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null) {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "请求postJson=$url 参数=${newMap}")

        RxHttp.postJson(url)
            .addAll(newMap)
            .toObservableResponseData<T>()
            .lifeOnMain(owner)
            .subscribe({ data -> reqResult?.invoke(CODE_SUCCESS, data, "") }, { error -> onFail(url, error, reqResult) })
    }

    /**
     * postBody提交
     * @param url           相对地址
     * @param map           参数
     */
    inline fun <reified T> postBody(owner: LifecycleOwner, url: String, map: ArrayMap<String, Any?>? = null, noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null) {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "请求postBody=$url 参数=${newMap}")

        RxHttp.postBody(url)
            .setBody(newMap)
            .toObservableResponseData<T>()
            .lifeOnMain(owner)
            .subscribe({ data -> reqResult?.invoke(CODE_SUCCESS, data, "") }, { error -> onFail(url, error, reqResult) })
    }

    /**
     * 文件路径上传 一般配合压缩路径提交
     */
    inline fun <reified T> uploadFilePath(
        owner: LifecycleOwner, url: String, file: String, map: ArrayMap<String, Any?>? = null,
        noinline progressListener: ((currentProgress: Int, currentSize: Long, totalSize: Long) -> Unit)? = null,
        noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null
    ) {
        uploadFile(owner, url, mutableListOf(File(file)), map, progressListener, reqResult)
    }

    /**
     * 文件路径上传 一般配合压缩路径提交
     */
    inline fun <reified T> uploadFile(
        owner: LifecycleOwner, url: String, file: File, map: ArrayMap<String, Any?>? = null,
        noinline progressListener: ((currentProgress: Int, currentSize: Long, totalSize: Long) -> Unit)? = null,
        noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null
    ) {
        uploadFile(owner, url, mutableListOf(file), map, progressListener, reqResult)
    }

    /**
     * 上传文件
     */
    inline fun <reified T> uploadFile(
        owner: LifecycleOwner, url: String, file: MutableList<File>, map: ArrayMap<String, Any?>? = null,
        noinline progressListener: ((currentProgress: Int, currentSize: Long, totalSize: Long) -> Unit)? = null,
        noinline reqResult: ((code: Int, data: T?, msg: String) -> Unit)? = null
    ) {
        val newMap = map ?: arrayMapOf()
        newMap.putAll(getCommonParamMap())
        if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "上传文件=$url 参数=${newMap}")

        RxHttp.postForm(url)
            .addAllQuery(newMap)
            .addFiles("file", file)
            .toObservableResponseData<T>()
            .onMainProgress {
                //上传进度回调,0-100，仅在进度有更新时才会回调
                val currentProgress = it.progress   //当前进度 0-100
                val currentSize = it.currentSize    //当前已上传的字节大小
                val totalSize = it.totalSize        //要上传的总字节大小
//                if (BuildConfig.DEBUG) {
//                    LogUtils.dTag(TAG, "currentProgress=$currentProgress currentSize=${ConvertUtils.byte2FitMemorySize(currentSize)} totalSize=${ConvertUtils.byte2FitMemorySize(totalSize)}")
//                }
                progressListener?.invoke(currentProgress, currentSize, totalSize)
            }
            .lifeOnMain(owner)    //页面销毁，自动关闭请求
            .subscribe({ data ->
                if (BuildConfig.DEBUG) LogUtils.dTag(TAG, "上传成功 data=$data")
                reqResult?.invoke(CODE_SUCCESS, data, "")
            }, { error -> onFail(url, error, reqResult) })
    }

    /**
     * 文件下载
     * @param destPath a/b/c.apk
     * @param isAppend 是否断点下载
     */
    fun downloadFile(
        owner: LifecycleOwner, url: String?, destPath: String, isAppend: Boolean = false,
        progressListener: ((currentProgress: Int, currentSize: Long, totalSize: Long) -> Unit)? = null,
        reqResult: ((code: Int, data: String?, msg: String) -> Unit)? = null
    ) {
        LogUtils.dTag(TAG, "下载文件=$url 下载存放地址=${destPath}")
        RxHttp.get(url)
            .toDownloadObservable(destPath, isAppend)
            .onMainProgress {
                val currentProgress = it.progress   //当前进度 0-100
                val currentSize = it.currentSize    //当前已下载的字节大小
                val totalSize = it.totalSize        //要下载的总字节大小

//                if (BuildConfig.DEBUG) {
//                    LogUtils.dTag(TAG, "currentProgress=$currentProgress currentSize=${ConvertUtils.byte2FitMemorySize(currentSize)} totalSize=${ConvertUtils.byte2FitMemorySize(totalSize)}")
//                }

                progressListener?.invoke(currentProgress, currentSize, totalSize)
            }
            .lifeOnMain(owner) //感知生命周期，并在主线程回调
            .subscribe({ data -> reqResult?.invoke(CODE_SUCCESS, data, "") }, { error -> onFail(url, error, reqResult) })
    }

    fun <T> onFail(url: String?, error: Throwable, reqResult: ((Int, T?, String) -> Unit)? = null) {
        try {
            if (error is ParseException) {
                val errorCode = error.errorCode.toInt()
                reqResult?.invoke(errorCode, null, "${error.message}")
                if (errorCode != CODE_SUCCESS) {
                    LogUtils.eTag(TAG, "1code=${errorCode},msg=${error.message},url=$url")
                }
            } else {
                reqResult?.invoke(CODE_FAIL, null, StringUtils.getString(com.libcommon.R.string.common_error_tips))
                LogUtils.eTag(TAG, "2code=${CODE_FAIL},msg=${error.message},url=$url")
                error.printStackTrace()
            }
        } catch (e: Exception) {
            e.printStackTrace()  //防止toInt错误
            reqResult?.invoke(CODE_FAIL, null, StringUtils.getString(com.libcommon.R.string.common_error_tips))
            LogUtils.eTag(TAG, "3code=${CODE_FAIL},msg=${e.message},url=$url")
        }
    }

    /**
     * http请求公共参数
     */
    @SuppressLint("MissingPermission")
    fun getCommonParamMap(): MutableMap<String, Any?> {
        mapCommonParams.apply {
            //动态参数
            putAll(userParamsCallback?.invoke() ?: arrayMapOf())

            put("t", TimeUtils.getNowMills())

            //静态参数
            if (!this.contains("os")) {
                put("os", "android")
                put("osVersion", DeviceUtils.getSDKVersionCode())
                put("appId", "nawa")
                put("model", DeviceUtils.getModel())
                put("appVersion", AppUtils.getAppVersionName())
                put("channel", "google")
                put("androidId", DeviceUtils.getAndroidID())
                put("timeZone", TimeZone.getDefault().id)
                networkChange()
            }

            put("netType", netType)
            put("ispType", ispType)
            put("hasVPN", hasVPN)
        }
        return mapCommonParams
    }

    private var netType = ""
    private var ispType = ""
    private var hasVPN = 0
    fun networkChange() {
        initNetType()
        initIspType()
        initHasVpn()
    }

    @SuppressLint("MissingPermission")
    private fun initNetType() {
        netType = NetworkUtils.getNetworkType().name
    }

    private fun initIspType() {
        ispType = NetworkUtils.getNetworkOperatorName()
    }

    private fun initHasVpn() {
        hasVPN = if (hasVPN()) 1 else 0
    }

    @SuppressLint("MissingPermission")
    private fun hasVPN(): Boolean {
        runCatching {
            val cm = Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val currentNetwork = cm.activeNetwork
            val networkCapabilities = cm.getNetworkCapabilities(currentNetwork) ?: return false
            return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        }
        return false
    }
}